//
// Created by twetec on 25-6-19.
//
#include <network_prober/sys_config/sys_config.h>

#include <fstream>
#include <boost/format.hpp>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <iomanip>
#include <google/protobuf/message_lite.h>
#include <libcpuid/libcpuid.h>
#include <sys/sysinfo.h>
#include <sys/statfs.h>
#include <filesystem>

static const std::string_view kBoardSerialFilePath = "/sys/class/dmi/id/board_serial";
static const std::string_view kProductSerialFilePath = "/sys/class/dmi/id/product_serial";
static const std::string_view kMachineIdFilePath = "/var/lib/dbus/machine-id";

static const std::string_view kConfigYamlPerceptionKey = "perception";
static const std::string_view kConfigYamlPerceptionIPKey = "ip";
static const std::string_view kConfigYamlPerceptionPortKey = "port";

namespace network_prober::sys_config
{
SysConfig &SysConfig::getInstance()
{
    static SysConfig instance;
    return instance;
}

void SysConfig::_initBoardSerial()
{
    // 读取配置文件
    std::ifstream file(kBoardSerialFilePath.data(), std::ifstream::in);

    // 如果文件打开失败，则抛出异常
    if (!file.is_open()) {
        auto errFmt = boost::format("文件打开失败：%1%") % kBoardSerialFilePath;
        SPDLOG_INFO("/sys/class/dmi/id/board_serial文件不存在..");
        return;
        // throw std::runtime_error(errFmt.str());
    }

    // 读取内容到字符串
    std::getline(file, m_boardSerial);

    // 关闭文件
    file.close();

    // 检查文件内容

    if (m_boardSerial.empty()) {
        auto errFmt = boost::format("文件内容为空：%1%") % kBoardSerialFilePath;
        throw std::runtime_error(errFmt.str());
    }

    // 整理读到的字符串内容，移除结尾的换行符
    if (m_boardSerial.back() == '\n') {
        m_boardSerial.pop_back();
    }

    SPDLOG_INFO("Board Serial: {}", m_boardSerial);
}

void SysConfig::_initProductSerial()
{
    // 读取配置文件
    std::ifstream file(kProductSerialFilePath.data(), std::ifstream::in);

    // 如果文件打开失败，则抛出异常
    if (!file.is_open()) {
        auto errFmt = boost::format("文件打开失败：%1%") % kProductSerialFilePath;
        throw std::runtime_error(errFmt.str());
    }

    // 读取内容到字符串
    std::getline(file, m_productSerial);

    // 关闭文件
    file.close();

    // 检查文件内容
    if (m_productSerial.empty()) {
        auto errFmt = boost::format("文件内容为空：%1%") % kProductSerialFilePath;
        SPDLOG_INFO("{}", errFmt.str());
        return;
    }

    // 整理读到的字符串内容，移除结尾的换行符
    if (m_productSerial.back() == '\n') {
        m_productSerial.pop_back();
    }

    SPDLOG_INFO("Product Serial: {}", m_productSerial);
}

void SysConfig::_initMachineId()
{
    // 读取配置文件
    std::ifstream file(kMachineIdFilePath.data(), std::ifstream::in);

    // 如果文件打开失败，则抛出异常
    if (!file.is_open()) {
        auto errFmt = boost::format("文件打开失败：%1%") % kMachineIdFilePath;
        throw std::runtime_error(errFmt.str());
    }

    // 读取内容到字符串
    std::getline(file, m_machineId);

    // 关闭文件
    file.close();

    // 检查文件内容
    if (m_machineId.empty()) {
        auto errFmt = boost::format("文件内容为空：%1%") % kMachineIdFilePath;
        throw std::runtime_error(errFmt.str());
    }

    // 整理读到的字符串内容，移除结尾的换行符
    if (m_machineId.back() == '\n') {
        m_machineId.pop_back();
    }

    SPDLOG_INFO("Machine ID: {}", m_machineId);
}

void SysConfig::_initProductUUID()
{
    // 读取配置文件
    std::ifstream file("/sys/class/dmi/id/product_uuid", std::ifstream::in);

    // 如果文件打开失败，则抛出异常
    if (!file.is_open()) {
        auto errFmt = boost::format("文件打开失败：%1%") % kMachineIdFilePath;
        throw std::runtime_error(errFmt.str());
    }

    // 读取内容到字符串
    std::getline(file, m_productUUID);

    // 关闭文件
    file.close();

    // 检查文件内容
    if (m_productUUID.empty()) {
        auto errFmt = boost::format("文件内容为空：/sys/class/dmi/id/product_uuid");
        throw std::runtime_error(errFmt.str());
    }

    // 整理读到的字符串内容，移除结尾的换行符
    if (m_productUUID.back() == '\n') {
        m_productUUID.pop_back();
    }

    SPDLOG_INFO("Product UUID: {}", m_productUUID);
}

void SysConfig::_initConfigYaml()
{
    // 加载配置文件
    m_configYaml = YAML::LoadFile(CONFIG_YAML_FILE_PATH);
    SPDLOG_INFO("ConfigYaml读取成功...");
}

void SysConfig::init()
{
    // 初始化 board_serial
    _initBoardSerial();
    // 初始化 product_serial
    _initProductSerial();
    // 初始化 product_uuid
    _initProductUUID();
    // 初始化 machine_id
    _initMachineId();
    // 初始化 iniConfig
    _initConfigYaml();
    // 初始化 cpu 信息
    _initProberCPUInfo();
}

std::string SysConfig::boardSerial()
{
    return m_boardSerial;
}

std::string SysConfig::productSerial()
{
    return m_productSerial;
}

std::string SysConfig::productUUID()
{
    return m_productUUID;
}

std::string SysConfig::machineId()
{
    return m_machineId;
}

std::string SysConfig::perceptionIP()
{
    YAML::Node config = m_configYaml[kConfigYamlPerceptionKey];
    return config[kConfigYamlPerceptionIPKey].as<std::string>();
}

size_t SysConfig::perceptionPort()
{
    YAML::Node config = m_configYaml[kConfigYamlPerceptionKey];
    return config[kConfigYamlPerceptionPortKey].as<size_t>();
}

std::string SysConfig::proberUUID()
{
    if (m_proberUUID.empty()) {
        // 字符串流，用于拼接
        std::stringstream ss;

        // 基础信息拼接
        if (!m_boardSerial.empty()) {
            ss << m_boardSerial;
        }
        // 合并
        ss << m_productUUID << m_machineId;

        // mac列表拼接
        std::vector<ProberInterfaceConf> proberInterfaceConfs = this->proberInterfaceConfs();
        for (auto it = proberInterfaceConfs.begin(); it != proberInterfaceConfs.end(); it++) {
            ss << it->macAddress;
        }

        // 加密后返回
        m_proberUUID = _SHA256Encrypt(ss.str());
    }

    return m_proberUUID;
}

std::string SysConfig::proberVersion()
{
    return m_configYaml["prober"]["version"].as<std::string>();
}

std::string SysConfig::proberPublicModel()
{
    return m_configYaml["prober"]["public-model"].as<std::string>();
}

std::vector<ProberInterfaceConf> SysConfig::proberInterfaceConfs()
{
    YAML::Node config = m_configYaml["interfaces"];

    std::vector<ProberInterfaceConf> proberInterfaceConfs;

    for (size_t i{0}, size{config.size()}; i < size; i++) {
        // 探针网口配置
        ProberInterfaceConf proberInterfaceConf;

        proberInterfaceConf.name = config[i]["name"].as<std::string>();

        proberInterfaceConf.IPv4Address = config[i]["ipv4"]["address"].as<std::string>();
        proberInterfaceConf.IPv4Gateway = config[i]["ipv4"]["gateway"].as<std::string>();
        proberInterfaceConf.IPv4SubnetMask = config[i]["ipv4"]["subnetMask"].as<std::string>();

        proberInterfaceConf.IPv6Address = config[i]["ipv6"]["address"].as<std::string>();
        proberInterfaceConf.IPv6Gateway = config[i]["ipv6"]["gateway"].as<std::string>();
        proberInterfaceConf.IPv6SubnetMask = config[i]["ipv6"]["subnetMask"].as<std::string>();

        proberInterfaceConf.bandwidth = config[i]["bandwidth"].as<std::string>();
        proberInterfaceConf.speed = config[i]["speed"].as<std::string>();
        proberInterfaceConf.macAddress = config[i]["macAddress"].as<std::string>();

        proberInterfaceConfs.emplace_back(proberInterfaceConf);
    }

    return proberInterfaceConfs;
}

std::string SysConfig::_SHA256Encrypt(const std::string& text)
{
    // 创建一个EVP_MD_CTX对象
    EVP_MD_CTX *evpCtx = EVP_MD_CTX_new();

    // 初始化SHA256上下文
    if (EVP_DigestInit_ex(evpCtx, EVP_sha256(), nullptr) != 1) {
        EVP_MD_CTX_free(evpCtx);
        throw std::runtime_error("SHA256上下文初始化失败...");
    }

    // 计算哈希值
    if (EVP_DigestUpdate(evpCtx, text.c_str(), text.size()) != 1) {
        EVP_MD_CTX_free(evpCtx);
        throw std::runtime_error("SHA256哈希值计算失败...");
    }

    // 字符串长度
    unsigned int hashLen = 0;
    // 返回结果（结果保存在result中）
    unsigned char hash[EVP_MAX_MD_SIZE] = {};

    // 计算结果
    if (EVP_DigestFinal_ex(evpCtx, hash, &hashLen) != 1) {
        EVP_MD_CTX_free(evpCtx);
        throw std::runtime_error("SHA256 加密失败...");
    }

    // 释放
    EVP_MD_CTX_free(evpCtx);

    // 将哈希值转换为十六进制字符串
    char hashHex[2 * EVP_MAX_MD_SIZE + 1]; // +1 for null terminator
    for (unsigned int i = 0; i < hashLen; i++) {
        sprintf(&hashHex[i*2], "%02x", hash[i]);
    }
    hashHex[2 * hashLen] = '\0';

    return std::string(hashHex);
}

void SysConfig::_initProberCPUInfo()
{
    // 检测探针主机是否支持cpu信息读取
    if (!cpuid_present()) {
        throw std::runtime_error("探针主机不支持cpu信息读取..");
    }

    // cpu 元数据
    struct cpu_raw_data_t raw;                                             // contains only raw data
    struct cpu_id_t data;

    // 读取 cpu 元数据
    if (cpuid_get_raw_data(&raw) < 0) {
        auto errFmt = boost::format("探针主机cpu元数据获取失败：%1%") % cpuid_error();
        throw std::runtime_error(errFmt.str());
    }

    // 将元数据解析成cpu_id的信息数据
    if (cpu_identify(&raw, &data) < 0) {
        auto errFmt = boost::format("探针主机cpu元数据解析失败：%1%") % cpuid_error();
        throw std::runtime_error(errFmt.str());
    }

    // cpu_id_t中包含了cpu的相关信息
    // CPU型号
    m_proberCPUModel = data.brand_str;
    // CPU核数
    m_proberCPUCores = data.num_cores;
}

std::string SysConfig::proberCPUModel()
{
    return m_proberCPUModel;
}

std::uint8_t SysConfig::proberCPUCores()
{
    return m_proberCPUCores;
}

std::string SysConfig::proberMemoryBytes()
{
    if (m_proberMemoryBytes.empty()) {
        struct sysinfo info;

        // 获取系统信息
        if (sysinfo(&info) < 0) {
            throw std::runtime_error("sysinfo信息获取失败...");
        }

        m_proberMemoryBytes = std::to_string(info.totalram);
    }

    return m_proberMemoryBytes;
}

std::string SysConfig::proberDiskBytes()
{
    if (m_proberDiskBytes.empty()) {
        struct statfs sfs;

        if (statfs("/", &sfs) < 0) {
            throw std::runtime_error("硬盘信息获取失败...");
        }

        // sfs.f_bsize: 每个块的字节数  sfs.f_blocks: 挂载点的总块数
        m_proberDiskBytes = std::to_string(sfs.f_bsize * sfs.f_blocks);
    }

    return m_proberDiskBytes;
}

void SysConfig::setProberSecretKey(const std::string& secretKey)
{
    m_proberSecretKey = secretKey;
}

std::string_view SysConfig::proberSecretKey()
{
    return m_proberSecretKey;
}

struct SysCPUInfo
{
    std::string name{};
    std::uint32_t user{};
    std::uint32_t nice{};
    std::uint32_t system{};
    std::uint32_t idle{};
    std::uint32_t iowait{};
    std::uint32_t irq{};
    std::uint32_t softirq{};
    std::uint32_t steal{};
    std::uint32_t guest{};
    std::uint32_t guest_nice{};
};

std::uint8_t SysConfig::proberCPUUsage()
{
    std::string procStatFilePath = "/proc/stat";

    std::ifstream file(procStatFilePath, std::ifstream::in);

    // 如果文件打开失败，则抛出异常
    if (!file.is_open()) {
        auto errFmt = boost::format("文件打开失败：%1%") % procStatFilePath;
        throw std::runtime_error(errFmt.str());
    }

    // 读取内容到字符串
    std::string line;
    std::getline(file, line);

    // 提取相应的值
    std::istringstream iss{line};
    SysCPUInfo initialCPUInfo;
    iss >> initialCPUInfo.name >> initialCPUInfo.user >> initialCPUInfo.nice >> initialCPUInfo.system
        >> initialCPUInfo.idle >> initialCPUInfo.iowait >> initialCPUInfo.irq >> initialCPUInfo.softirq
        >> initialCPUInfo.steal >> initialCPUInfo.guest >> initialCPUInfo.guest_nice;

    // 关闭连接
    file.close();

    // 睡眠1秒
    std::this_thread::sleep_for(std::chrono::seconds(1));

    // 再次打开文件
    file.open(procStatFilePath, std::ifstream::in);

    // 清空字符串内容，将内部字符数组长度设为 0，但不会释放底层分配的内存
    // 字符串内容变为空，但字符串所占用的内存空间仍存在，直到字符串被重新赋值或销毁时才会释放
    line.clear();
    std::getline(file, line);

    // 再次提取cpu信息
    iss.clear();
    iss = std::istringstream{line};

    // 再次存储cpu信息
    SysCPUInfo finalCPUInfo;
    iss >> finalCPUInfo.name >> finalCPUInfo.user >> finalCPUInfo.nice >> finalCPUInfo.system
        >> finalCPUInfo.idle >> finalCPUInfo.iowait >> finalCPUInfo.irq >> finalCPUInfo.softirq
        >> finalCPUInfo.steal >> finalCPUInfo.guest >> finalCPUInfo.guest_nice;

    // 清空缓存
    line.clear();
    iss.clear();
    file.close();

    // 计算cpu利用率
    std::uint32_t initialTotalTime = initialCPUInfo.user + initialCPUInfo.nice + initialCPUInfo.system + initialCPUInfo.idle + initialCPUInfo.iowait + initialCPUInfo.irq + initialCPUInfo.softirq + initialCPUInfo.steal + initialCPUInfo.guest + initialCPUInfo.guest_nice;
    std::uint32_t initialIdleTime = initialCPUInfo.idle;
    std::uint32_t initialOccupiedTime = initialTotalTime - initialIdleTime;
    std::uint32_t finalTotalTime = finalCPUInfo.user + finalCPUInfo.nice + finalCPUInfo.system + finalCPUInfo.idle + finalCPUInfo.iowait + finalCPUInfo.irq + finalCPUInfo.softirq + finalCPUInfo.steal + finalCPUInfo.guest + finalCPUInfo.guest_nice;
    std::uint32_t finalIdleTime = finalCPUInfo.idle;
    std::uint32_t finalOccupiedTime = finalTotalTime - finalIdleTime;

    // 利用的量 / 总量
    return static_cast<std::float_t>(finalOccupiedTime - initialOccupiedTime) / (finalTotalTime - initialTotalTime) * 100;
}

std::uint8_t SysConfig::proberMemoryUsage()
{
    std::string procMemInfoFilePath = "/proc/meminfo";

    std::ifstream file(procMemInfoFilePath, std::ifstream::in);

    // 如果文件打开失败，则抛出异常
    if (!file.is_open()) {
        auto errFmt = boost::format("文件打开失败：%1%") % procMemInfoFilePath;
        throw std::runtime_error(errFmt.str());
    }

    std::unordered_map<std::string, std::uint64_t> memInfos;

    std::string memInfoKey{};
    std::uint64_t memInfoValue{};

    while (file >> memInfoKey >> memInfoValue) {
        memInfos[memInfoKey] = memInfoValue;
        file.ignore(256, '\n');
    }

    std::uint64_t totalMem = memInfos.at("MemTotal:");
    std::uint64_t availableMem = memInfos.at("MemAvailable:");

    return static_cast<std::float_t>(totalMem - availableMem) / totalMem * 100;
}

std::uint8_t SysConfig::proberDiskUsage()
{
    // 设定要计算空间的路径
    std::filesystem::path diskPath{"/"};
    // 计算存储空间
    std::filesystem::space_info spaceInfo = std::filesystem::space(diskPath);
    // (总空间 - 可用空间) / 总空间
    return static_cast<std::float_t>(spaceInfo.capacity - spaceInfo.free) / spaceInfo.capacity * 100;
}

std::uint32_t SysConfig::proberDiskSpaceGB()
{
    // 设定要计算空间的路径
    std::filesystem::path diskPath{"/"};
    // 计算存储空间
    std::filesystem::space_info spaceInfo = std::filesystem::space(diskPath);
    // 总空间
    return static_cast<std::float_t>(spaceInfo.capacity) / (1024 * 1024 * 1024);
}

SysConfig::SysConfig()
{
    // if (rc < 0) {
    //     SPDLOG_ERROR("Config File Loading Failed: {}", rc);
    //     throw std::runtime_error("Config File Loading Failed");
    // }
    //
    // CSimpleIniA::TNamesDepend sections;
    // ini.GetAllSections(sections);
    //
    // const char* pv;
    // pv = ini.GetValue("DuckDB", "host");
    //
    // std::cout << pv << std::endl;
}

SysConfig::~SysConfig()
{
}
}
